<?php

namespace RedMVC\Drawing;
use RedMVC;

/**
 * Color class
 *
 * @category    RedMVC
 * @package     Drawing 
 * @author      Jan Fischer, bitWorking <info@bitworking.de>
 */
class Color{

    const CIRCLE_DIRECTION_LEFT     = 'left';
    const CIRCLE_DIRECTION_RIGHT    = 'right';

    protected $_hex;
    
    protected static $_names = array(
        'aliceblue' => '#f0f8ff',
        'antiquewhite' => '#faebd7',
        'aqua' => '#00ffff',
        'aquamarine' => '#7fffd4',
        'azure' => '#f0ffff',
        'beige' => '#f5f5dc',
        'bisque' => '#ffe4c4',
        'black' => '#000000',
        'blanchedalmond' => '#ffebcd',
        'blue' => '#0000ff',
        'blueviolet' => '#8a2be2',
        'brown' => '#a52a2a',
        'burlywood' => '#deb887',
        'cadetblue' => '#5f9ea0',
        'chartreuse' => '#7fff00',
        'chocolate' => '#d2691e',
        'coral' => '#ff7f50',
        'cornflowerblue' => '#6495ed',
        'cornsilk' => '#fff8dc',
        'crimson' => '#dc143c',
        'cyan' => '#00ffff',
        'darkblue' => '#00008b',
        'darkcyan' => '#008b8b',
        'darkgoldenrod' => '#b8860b',
        'darkgray' => '#a9a9a9',
        'darkgrey' => '#a9a9a9',
        'darkgreen' => '#006400',
        'darkkhaki' => '#bdb76b',
        'darkmagenta' => '#8b008b',
        'darkolivegreen' => '#556B2F',
        'darkorange' => '#ff8c00',
        'darkorchid' => '#9932cc',
        'darkred' => '#8b0000',
        'darksalmon' => '#e9967a',
        'darkseagreen' => '#8fbc8f',
        'darkslateblue' => '#483d8b',
        'darkslategray' => '#2f4f4f',
        'darkslategrey' => '#2f4f4f',
        'darkturquoise' => '#00ced1',
        'darkviolet' => '#9400d3',
        'deeppink' => '#ff1493',
        'deepskyblue' => '#00bfff',
        'dimgray' => '#696969',
        'dimgrey' => '#696969',
        'dodgerblue' => '#1e90ff',
        'firebrick' => '#b22222',
        'floralwhite' => '#fffaf0',
        'forestgreen' => '#228b22',
        'fuchsia' => '#ff00ff',
        'gainsboro' => '#dcdcdc',
        'ghostwhite' => '#f8f8ff',
        'gold' => '#ffd700',
        'goldenrod' => '#daa520',
        'gray' => '#808080',
        'grey' => '#808080',
        'green' => '#008000',
        'greenyellow' => '#adff2f',
        'honeydew' => '#f0fff0',
        'hotpink' => '#ff69b4',
        'indianred' => '#cd5c5c',
        'indigo' => '#4b0082',
        'ivory' => '#fffff0',
        'khaki' => '#f0e68c',
        'lavender' => '#e6e6fa',
        'lavenderblush' => '#fff0f5',
        'lawngreen' => '#7cfc00',
        'lemonchiffon' => '#fffacd',
        'lightblue' => '#add8e6',
        'lightcoral' => '#f08080',
        'lightcyan' => '#e0ffff',
        'lightgoldenrodyellow' => '#fafad2',
        'lightgray' => '#d3d3d3',
        'lightgrey' => '#d3d3d3',
        'lightgreen' => '#90ee90',
        'lightpink' => '#ffb6c1',
        'lightsalmon' => '#ffa07a',
        'lightseagreen' => '#20b2aa',
        'lightskyblue' => '#87cefa',
        'lightslategray' => '#778899',
        'lightslategrey' => '#778899',
        'lightsteelblue' => '#b0c4de',
        'lightyellow' => '#ffffe0',
        'lime' => '#00ff00',
        'limegreen' => '#32cd32',
        'linen' => '#faf0e6',
        'magenta' => '#ff00ff',
        'maroon' => '#800000',
        'mediumaquamarine' => '#66cdaa',
        'mediumblue' => '#0000cd',
        'mediumorchid' => '#ba55d3',
        'mediumpurple' => '#9370db',
        'mediumseagreen' => '#3cb371',
        'mediumslateblue' => '#7b68ee',
        'mediumspringgreen' => '#00fa9a',
        'mediumturquoise' => '#48d1cc',
        'mediumvioletred' => '#c71585',
        'midnightblue' => '#191970',
        'mintcream' => '#f5fffa',
        'mistyrose' => '#ffe4e1',
        'moccasin' => '#ffe4b5',
        'navajowhite' => '#ffdead',
        'navy' => '#000080',
        'oldlace' => '#fdf5e6',
        'olive' => '#808000',
        'olivedrab' => '#6b8e23',
        'orange' => '#ffa500',
        'orangered' => '#ff4500',
        'orchid' => '#da70d6',
        'palegoldenrod' => '#eee8aa',
        'palegreen' => '#98fb98',
        'paleturquoise' => '#afeeee',
        'palevioletred' => '#db7093',
        'papayawhip' => '#ffefd5',
        'peachpuff' => '#ffdab9',
        'peru' => '#cd853f',
        'pink' => '#ffc0cb',
        'plum' => '#dda0dd',
        'powderblue' => '#b0e0e6',
        'purple' => '#800080',
        'red' => '#ff0000',
        'rosybrown' => '#bc8f8f',
        'royalblue' => '#4169e1',
        'saddlebrown' => '#8b4513',
        'salmon' => '#fa8072',
        'sandybrown' => '#f4a460',
        'seagreen' => '#2e8b57',
        'seashell' => '#fff5ee',
        'sienna' => '#a0522d',
        'silver' => '#c0c0c0',
        'skyblue' => '#87ceeb',
        'slateblue' => '#6a5acd',
        'slategray' => '#708090',
        'slategrey' => '#708090',
        'snow' => '#fffafa',
        'springgreen' => '#00ff7f',
        'steelblue' => '#4682b4',
        'tan' => '#d2b48c',
        'teal' => '#008080',
        'thistle' => '#d8bfd8',
        'tomato' => '#ff6347',
        'turquoise' => '#40e0d0',
        'violet' => '#ee82ee',
        'wheat' => '#f5deb3',
        'white' => '#ffffff',
        'whitesmoke' => '#f5f5f5',
        'yellow' => '#ffff00',
        'yellowgreen' => '#9acd32'
    );
    
    private function __construct(){
    
    }
    
    public static function fromHex($hex){
        $hex            = new Color\Hex($hex);
        $color          = new self();
        $color->_hex    = $hex;
        return $color;
    }
    
    public static function fromRgb($r, $g, $b){
        $rgb            = new Color\Rgb($r, $g, $b);
        $color          = new self();
        $color->_hex    = self::rgb2hex($rgb);
        return $color;
    }
    
    public static function fromHsb($h, $s, $b){
        $hsb            = new Color\Hsb($h, $s, $b);
        $color          = new self();
        $color->_hex    = self::hsb2hex($hsb);
        return $color;
    }

    public static function fromCmyk($c, $m, $y, $k){
        $cmyk           = new Color\Cmyk($c, $m, $y, $k);
        $color          = new self();
        $color->_hex    = self::cmyk2hex($cmyk);
        return $color;
    }
    
    public static function fromName($name){
        if(isset(self::$_names[strtolower($name)])){
            $hex = self::$_names[strtolower($name)];
            return self::fromHex($hex);
        }
        throw new \Exception('Color: name does not exist');
    }
    
    public static function random($h = null, $s = null, $b = null){
        if(null === $h){
            $h = RedMVC\Random::digit(0, 359);
        }
        if(null === $s){
            $s = RedMVC\Random::digit(0, 100);
        }
        if(null === $b){
            $b = RedMVC\Random::digit(0, 100);
        }
        $color = self::fromHsb($h, $s, $b);
        return $color;
    }
    
    public static function invert(Color $color){
        return self::fromHex($color->_hex->hex ^ 0xffffff);
    }
    
    public function circle(Color $color, $steps, $degree, $direction = self::CIRCLE_DIRECTION_RIGHT){
        $hsb        = self::hex2hsb($color->_hex);
        $colors     = array();
		$hueStart   = $hsb->h;
		$hueWidth   = $degree / ($steps-1);
        $colors[]   = $color;
		for($i = 1;$i < $steps;$i++){
			if($direction == self::CIRCLE_DIRECTION_RIGHT){
				$hue = $hueStart + ($hueWidth * $i);
			}
			else if($direction == self::CIRCLE_DIRECTION_LEFT){
				$hue = $hueStart - ($hueWidth * $i);
			}
			if($hue > 360){
				$hue -= 360;
			}
			else if($hue < 0){
				$hue += 360;
			}
			$colors[] = self::fromHsb($hue, $hsb->s, $hsb->b);
		}
		return $colors;
    }
    
    /**
     * mix two colors
     *
     * @param Color $color1
     * @param Color $color2
     * @param float $percent (0-1)
     * @return Color
     */
    public static function mix(Color $color1, Color $color2, $percent){
        $c1Rgb      = $color1->toRgb();
        $c2Rgb      = $color2->toRgb();
        $amountC1   = (float) 1.0 - $percent;                
        return self::fromRgb(            
            (int) round($c1Rgb->r * $amountC1 + $c2Rgb->r * $percent),
            (int) round($c1Rgb->g * $amountC1 + $c2Rgb->g * $percent),
            (int) round($c1Rgb->b * $amountC1 + $c2Rgb->b * $percent)
        );
    }
    
    /**
     * gradient from one color to another
     *
     * @param Color $from
     * @param Color $to
     * @param int $steps
     * @return array array with gradient colors
     */
    public static function gradientFromTo(Color $from, Color $to, $steps){
        $steps -= 1;
        $colors = array();
        for($i=0;$i<=$steps;$i++){
            $colors[] = self::mix($from, $to, $i/$steps);
        }
        return $colors;
    }
    
    /**
     * gradient between multiple colors
     *
     * @param array $colors array of colors
     * @param int $steps
     * @return array array with gradient colors
     */
    public static function gradient(array $colors, $steps){        
        $colorCount     = count($colors)-1;
        $stepsPerColor  = floor($steps/$colorCount);
        $remain         = $steps%$stepsPerColor;
        $gradient       = array();
		for($i=0;$i<$colorCount;$i++){
            if($i==($colorCount-1)){$stepsPerColor+=$remain;}
            $gradientFromTo = self::gradientFromTo($colors[$i], $colors[$i+1], $stepsPerColor);
            $gradient       = array_merge($gradient, $gradientFromTo);		
		}
		return $gradient;
    }
    
    public function __toString(){
        return $this->_hex->__toString();
    }
    
    public function toHex(){
        return $this->_hex;
    }
    
    public function toRgb(){
        return self::hex2rgb($this->_hex);
    }
    
    public function toHsb(){
        return self::hex2hsb($this->_hex);
    }
    
    public function toCmyk(){
        return self::hex2cmyk($this->_hex);
    }

    public static function rgb2hsb(Color\Rgb $rgb){
        $rgb = Color\Rgb::normalize($rgb);        
        $max = max($rgb->r, $rgb->g, $rgb->b);
        $min = min($rgb->r, $rgb->g, $rgb->b);        
        // h
        if($max === $min){
            $h = 0;
        }
        else if($max === $rgb->r){
            $h = 60 * (($rgb->g - $rgb->b)/($max - $min));
        }
        else if($max === $rgb->g){
            $h = 60 * (2 + (($rgb->b - $rgb->r)/($max - $min)));
        }
        else if($max === $rgb->b){
            $h = 60 * (4 + (($rgb->r - $rgb->g)/($max - $min)));
        }
        if($h < 0){
            $h += 360;
        }        
        // s
        if($max === 0){
            $s = 0;
        }
        else{
            $s = ($max - $min) / $max;
        }        
        // b
        $b = $max;
        return new Color\Hsb(round($h), round($s * 100), round($b * 100));
    }
    
    public static function hsb2rgb(Color\Hsb $hsb){
        $hsb = Color\Hsb::normalize($hsb);
        if($hsb->s == 0){
            $rgb = array($hsb->b, $hsb->b, $hsb->b);
        }
        else{                  
            $hi = (int) ($hsb->h / 60);
            $f  = ($hsb->h / 60) - $hi;
            $p  = $hsb->b * (1 - $hsb->s);
            $q  = $hsb->b * (1 - $hsb->s * $f);
            $t  = $hsb->b * (1 - $hsb->s * (1 - $f));
            
            if($hi === 0 || $hi === 6){
                $rgb = array($hsb->b, $t, $p);
            }
            else if($hi === 1){
                $rgb = array($q, $hsb->b, $p);
            }
            else if($hi === 2){
                $rgb = array($p, $hsb->b, $t);
            }
            else if($hi === 3){
                $rgb = array($p, $q, $hsb->b);
            }
            else if($hi === 4){
                $rgb = array($t, $p, $hsb->b);
            }
            else if($hi === 5){
                $rgb = array($hsb->b, $p, $q);
            }
        }
        return new Color\Rgb(round($rgb[0] * 255), round($rgb[1] * 255), round($rgb[2] * 255));
    }
    
    public static function hex2rgb(Color\Hex $hex){
        $r = 0xff & ($hex->hex >> 0x10);
        $g = 0xff & ($hex->hex >> 0x8);
        $b = 0xff & $hex->hex;
        return new Color\Rgb($r, $g, $b);
    }
    
    public static function rgb2hex(Color\Rgb $rgb){
        return new Color\Hex(
            str_pad(dechex($rgb->r), 2, '0', STR_PAD_LEFT).
            str_pad(dechex($rgb->g), 2, '0', STR_PAD_LEFT).
            str_pad(dechex($rgb->b), 2, '0', STR_PAD_LEFT)
        );
    }
    
    public static function hsb2hex(Color\Hsb $hsb){
        $rgb = self::hsb2rgb($hsb);
        return self::rgb2hex($rgb);
    }
    
    public static function hex2hsb(Color\Hex $hex){
        $rgb = self::hex2rgb($hex);
        return self::rgb2hsb($rgb);
    }
    
    public static function rgb2cmyk(Color\Rgb $rgb){
        // rgb -> cmy
        $c = 1 - ($rgb->r / 255);
        $m = 1 - ($rgb->g / 255);
        $y = 1 - ($rgb->b / 255);
        
        // cmy -> cmyk
        $_k = 1;
        if($c < $_k) $_k = $c;
        if($m < $_k) $_k = $m;
        if($y < $_k) $_k = $y;
        if($_k == 1){
            $c = 0;
            $m = 0;
            $y = 0;
        }
        else{
            $c = ($c - $_k) / (1 - $_k);
            $m = ($m - $_k) / (1 - $_k);
            $y = ($y - $_k) / (1 - $_k);
        }
        $k = $_k;
        return new Color\Cmyk(round($c * 100), round($m * 100), round($y * 100), round($k * 100));
    }
    
    public static function cmyk2rgb(Color\Cmyk $cmyk){
        // cmyk -> cmy
        $cmyk = Color\Cmyk::normalize($cmyk);
        $c = ($cmyk->c * (1 - $cmyk->k) + $cmyk->k);
        $m = ($cmyk->m * (1 - $cmyk->k) + $cmyk->k);
        $y = ($cmyk->y * (1 - $cmyk->k) + $cmyk->k);
        
        // cmy -> rgb
        $r = round((1 - $c) * 255);
        $g = round((1 - $m) * 255);
        $b = round((1 - $y) * 255);
        return new Color\Rgb($r, $g, $b);
    }
    
    public static function cmyk2hex(Color\Cmyk $cmyk){
        $rgb = self::cmyk2rgb($cmyk);
        return self::rgb2hex($rgb);
    }
    
    public static function hex2cmyk(Color\Hex $hex){
        $rgb = self::hex2rgb($hex);
        return self::rgb2cmyk($rgb);
    }

}